<?php
/**
 * DashboardService.php
 *
 * @copyright   2023 thingsworld.com.cn - All Rights Reserved
 * @link        http://thingsworld.com.cn/
 * @author      stiffer.chen <chenl@thingsworld.com.cn>
 * @created     2023-03-10 09:33:14
 * @modified    2023-03-10 09:33:14
 *
 */

namespace App\Services;

use App\Library\Logger;
use App\Models\Event;
use App\Models\WorkOrder;
use App\Repositories\EventRepo;
use App\Repositories\WorkOrderRepo;
use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Illuminate\Support\Facades\DB;

class DashboardService extends BaseService
{
    private $logger;

    const DATE_TYPE_WEEK = 'week';
    const DATE_TYPE_MONTH = 'month';
    const DATE_TYPE_QUARTER = 'quarter';
    const DATE_TYPE_YEAR = 'year';
    const DATE_TYPE_MONTH_SELECT = 'month_select';

    const TYPE_ALL = 'all';
    const TYPE_CREATOR = 'creator';
    const TYPE_ASSIST = 'assist';
    const TYPE_MASTER = 'master';

    const MONTH_START = 'month_start';
    const MONTH_END = 'month_end';

    private $userId;

    public function __construct()
    {
        $this->logger = new Logger('service/dashboard');
        $this->userId = registry('user_id');
    }

    public function types()
    {
        return [
            self::TYPE_ALL,
            self::TYPE_CREATOR,
            self::TYPE_ASSIST,
            self::TYPE_MASTER
        ];
    }

    public function all()
    {
        // 指标统计
        $target = $this->target();
        // 待办事项
        $todo = $this->todo();
        // 满意度构成
        $satisfactionForm = $this->satisfactionForm();
        // 满意度好评率
        $satisfactionProbability = $this->satisfactionProbability();
        // 平均服务用时
        $avgServiceTime = $this->avgServiceTime();
        // 及时关单率
        $closeProbability = $this->closeProbability();
        // 服务人效
        $personEfficiency = $this->personEfficiency();

        return [
            'target' => $target,
            'todo' => $todo,
            'satisfaction_form' => $satisfactionForm,
            'satisfaction_probability' => $satisfactionProbability,
            'avg_service_time' => $avgServiceTime,
            'close_probability' => $closeProbability,
            'person_efficiency' => $personEfficiency
        ];
    }

    /**
     * 服务人效
     * 已完成工单数量 ÷ 总工作日
     * @param string $type
     * @param string $monthStart
     * @param string $monthEnd
     */
    public function personEfficiency(string $type = self::DATE_TYPE_MONTH_SELECT, string $monthStart = '', string $monthEnd = '')
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        if ($type == self::DATE_TYPE_MONTH_SELECT) {
            $data = $this->getPersonEfficiencyMonthSelect($type, $monthStart, $monthEnd);
            return [
                'type' => $type,
                'month_start' => $monthStart,
                'month_end' => $monthEnd,
                'labels' => $xAxis,
                'data' => $data
            ];
        }
        if ($type == self::DATE_TYPE_WEEK) {
            $data = $this->getPersonEfficiencyWeek($type, $monthStart, $monthEnd);
            return [
                'type' => $type,
                'month_start' => $monthStart,
                'month_end' => $monthEnd,
                'labels' => $xAxis,
                'data' => $data
            ];
        }
        if ($type == self::DATE_TYPE_MONTH) {
            $data = $this->getPersonEfficiencyMonth($type, $monthStart, $monthEnd);
            return [
                'type' => $type,
                'month_start' => $monthStart,
                'month_end' => $monthEnd,
                'labels' => $xAxis,
                'data' => $data
            ];
        }
        if ($type == self::DATE_TYPE_QUARTER) {
            $data = $this->getPersonEfficiencyQuarter($type, $monthStart, $monthEnd);
            return [
                'type' => $type,
                'month_start' => $monthStart,
                'month_end' => $monthEnd,
                'labels' => $xAxis,
                'data' => $data
            ];
        }
        if ($type == self::DATE_TYPE_YEAR) {
            $data = $this->getPersonEfficiencyYear($type, $monthStart, $monthEnd);
            return [
                'type' => $type,
                'month_start' => $monthStart,
                'month_end' => $monthEnd,
                'labels' => $xAxis,
                'data' => $data
            ];
        }
    }

    private function getPersonEfficiencyYear($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $data = [];

        $builder = WorkOrder::query()
            ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE)
            ->where('work_order.update_time', '>=', strtotime($start))
            ->where('work_order.update_time', '<=', strtotime($end));

        $workOrderType = trim(request('work_order_type', ''));
        $workOrderTypes = WorkOrderRepo::types();
        if (!in_array($workOrderType, $workOrderTypes)) {
            $workOrderType = '';
        }
        $deptId = intval(request('dept_id', ''));

        if ($workOrderType) {
            $builder->where('work_order.type', $workOrderType);
        }
        if ($deptId) {
            $builder->join('customer', 'customer.id', '=', 'work_order.customer_id');
            $builder->where('customer.department_id', $deptId);
        }

        $workOrderData = $builder->get();
        foreach ($xAxis as $item) {
            $itemStart = Carbon::parse($item)->startOfMonth()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfMonth()->toDateString() . ' 23:59:59';
            $completeWorkOrder = $workOrderData
                ->where('status', WorkOrderRepo::STATUS_COMPLETE)
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd);
            $completeWorkOrderCount = $completeWorkOrder->count();
            $completeIds = $completeWorkOrder->pluck('id')->toArray();
            $completeHistoryData = WorkOrder\StatusHistory::query()
                ->whereIn('work_order_id', $completeIds)
                ->get()
                ->groupBy('status')
                ->toArray();
            $createdData = $completeHistoryData[WorkOrderRepo::STATUS_CREATED] ?? [];
            $completeData = $completeHistoryData[WorkOrderRepo::STATUS_COMPLETE] ?? [];
            $completeData = collect($completeData)->keyBy('work_order_id') ->toArray();
            $totalDays = 0;
            foreach ($createdData as $createdItem) {
                $createdWorkOrderId = $createdItem['work_order_id'];
                $tempCompleteWorkOrder = $completeData[$createdWorkOrderId] ?? [];
                if (!$tempCompleteWorkOrder) {
                    continue;
                }
                $tempStartTime = $createdItem['update_time_format'];
                $tempCompleteTime = $tempCompleteWorkOrder['update_time_format'];
                $diffDay = Carbon::parse($tempStartTime)->diffInDays($tempCompleteTime, true);
                $totalDays += $diffDay;
            }

            if ($completeWorkOrderCount && $totalDays) {
                $itemTotal = round($completeWorkOrderCount / $totalDays, 2);
            } else {
                $itemTotal = 0;
            }
            $data[] = $itemTotal;
        }
        return $data;
    }

    private function getPersonEfficiencyQuarter($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $data = [];

        $builder = WorkOrder::query()
            ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE)
            ->where('work_order.update_time', '>=', strtotime($start))
            ->where('work_order.update_time', '<=', strtotime($end));

        $workOrderType = trim(request('work_order_type', ''));
        $workOrderTypes = WorkOrderRepo::types();
        if (!in_array($workOrderType, $workOrderTypes)) {
            $workOrderType = '';
        }
        $deptId = intval(request('dept_id', ''));

        if ($workOrderType) {
            $builder->where('work_order.type', $workOrderType);
        }
        if ($deptId) {
            $builder->where('customer.department_id', $deptId);
        }

        $workOrderData = $builder->get();
        foreach ($xAxis as $item) {
            $itemStart = Carbon::parse($item)->startOfMonth()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfMonth()->toDateString() . ' 23:59:59';
            $completeWorkOrder = $workOrderData
                ->where('status', WorkOrderRepo::STATUS_COMPLETE)
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd);
            $completeWorkOrderCount = $completeWorkOrder->count();
            $completeIds = $completeWorkOrder->pluck('id')->toArray();
            $completeHistoryData = WorkOrder\StatusHistory::query()
                ->whereIn('work_order_id', $completeIds)
                ->get()
                ->groupBy('status')
                ->toArray();
            $createdData = $completeHistoryData[WorkOrderRepo::STATUS_CREATED] ?? [];
            $completeData = $completeHistoryData[WorkOrderRepo::STATUS_COMPLETE] ?? [];
            $completeData = collect($completeData)->keyBy('work_order_id') ->toArray();
            $totalDays = 0;
            foreach ($createdData as $createdItem) {
                $createdWorkOrderId = $createdItem['work_order_id'];
                $tempCompleteWorkOrder = $completeData[$createdWorkOrderId] ?? [];
                if (!$tempCompleteWorkOrder) {
                    continue;
                }
                $tempStartTime = $createdItem['update_time_format'];
                $tempCompleteTime = $tempCompleteWorkOrder['update_time_format'];
                $diffDay = Carbon::parse($tempStartTime)->diffInDays($tempCompleteTime, true);
                $totalDays += $diffDay;
            }

            if ($completeWorkOrderCount && $totalDays) {
                $itemTotal = round($completeWorkOrderCount / $totalDays, 2);
            } else {
                $itemTotal = 0;
            }
            $data[] = $itemTotal;
        }
        return $data;
    }

    private function getPersonEfficiencyMonth($type, $monthStart, $monthEnd)
    {
        $start = Carbon::now()->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::now()->endOfMonth()->toDateString() . ' 23:59:59';
        $data = [];
        $weeks = [
            Carbon::parse($start)->toDateString(),
            Carbon::parse($start)->addDays(7)->toDateString(),
            Carbon::parse($start)->addDays(14)->toDateString(),
            Carbon::parse($start)->addDays(21)->toDateString()
        ];

        $builder = WorkOrder::query()
            ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE)
            ->where('work_order.update_time', '>=', strtotime($start))
            ->where('work_order.update_time', '<=', strtotime($end));

        $workOrderType = trim(request('work_order_type', ''));
        $workOrderTypes = WorkOrderRepo::types();
        if (!in_array($workOrderType, $workOrderTypes)) {
            $workOrderType = '';
        }
        $deptId = intval(request('dept_id', ''));

        if ($workOrderType) {
            $builder->where('work_order.type', $workOrderType);
        }
        if ($deptId) {
            $builder->where('customer.department_id', $deptId);
        }

        $workOrderData = $builder->get();
        foreach ($weeks as $item) {
            $itemStart = Carbon::parse($item)->startOfWeek()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfWeek()->toDateString() . ' 23:59:59';
            $completeWorkOrder = $workOrderData
                ->where('status', WorkOrderRepo::STATUS_COMPLETE)
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd);
            $completeWorkOrderCount = $completeWorkOrder->count();
            $completeIds = $completeWorkOrder->pluck('id')->toArray();
            $completeHistoryData = WorkOrder\StatusHistory::query()
                ->whereIn('work_order_id', $completeIds)
                ->get()
                ->groupBy('status')
                ->toArray();
            $createdData = $completeHistoryData[WorkOrderRepo::STATUS_CREATED] ?? [];
            $completeData = $completeHistoryData[WorkOrderRepo::STATUS_COMPLETE] ?? [];
            $completeData = collect($completeData)->keyBy('work_order_id') ->toArray();
            $totalDays = 0;
            foreach ($createdData as $createdItem) {
                $createdWorkOrderId = $createdItem['work_order_id'];
                $tempCompleteWorkOrder = $completeData[$createdWorkOrderId] ?? [];
                if (!$tempCompleteWorkOrder) {
                    continue;
                }
                $tempStartTime = $createdItem['update_time_format'];
                $tempCompleteTime = $tempCompleteWorkOrder['update_time_format'];
                $diffDay = Carbon::parse($tempStartTime)->diffInDays($tempCompleteTime, true);
                $totalDays += $diffDay;
            }

            if ($completeWorkOrderCount && $totalDays) {
                $itemTotal = round($completeWorkOrderCount / $totalDays, 2);
            } else {
                $itemTotal = 0;
            }
            $data[] = $itemTotal;
        }
        return $data;
    }

    private function getPersonEfficiencyWeek($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = $xAxis[0] . ' 00:00:00';
        $end = $xAxis[count($xAxis) - 1] . ' 23:59:59';
        $data = [];

        $builder = WorkOrder::query()
            ->join('customer', 'customer.id', '=', 'work_order.customer_id')
            ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE)
            ->where('work_order.update_time', '>=', strtotime($start))
            ->where('work_order.update_time', '<=', strtotime($end));
        $workOrderType = trim(request('work_order_type', ''));
        $workOrderTypes = WorkOrderRepo::types();
        if (!in_array($workOrderType, $workOrderTypes)) {
            $workOrderType = '';
        }
        $deptId = intval(request('dept_id', ''));

        if ($workOrderType) {
            $builder->where('work_order.type', $workOrderType);
        }
        if ($deptId) {
            $builder->where('customer.department_id', $deptId);
        }

        $workOrderData = $builder->get();
        foreach ($xAxis as $item) {
            $itemStart = Carbon::parse($item)->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->toDateString() . ' 23:59:59';
            $completeWorkOrder = $workOrderData
                ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE)
                ->where('work_order.update_time', '>=', $itemStart)
                ->where('work_order.update_time', '<=', $itemEnd);
            $completeWorkOrderCount = $completeWorkOrder->count();
            $completeIds = $completeWorkOrder->pluck('id')->toArray();
            $completeHistoryData = WorkOrder\StatusHistory::query()
                ->whereIn('work_order_id', $completeIds)
                ->get()
                ->groupBy('status')
                ->toArray();
            $createdData = $completeHistoryData[WorkOrderRepo::STATUS_CREATED] ?? [];
            $completeData = $completeHistoryData[WorkOrderRepo::STATUS_COMPLETE] ?? [];
            $completeData = collect($completeData)->keyBy('work_order_id') ->toArray();
            $totalDays = 0;
            foreach ($createdData as $createdItem) {
                $createdWorkOrderId = $createdItem['work_order_id'];
                $tempCompleteWorkOrder = $completeData[$createdWorkOrderId] ?? [];
                if (!$tempCompleteWorkOrder) {
                    continue;
                }
                $tempStartTime = $createdItem['update_time_format'];
                $tempCompleteTime = $tempCompleteWorkOrder['update_time_format'];
                $diffDay = Carbon::parse($tempStartTime)->diffInDays($tempCompleteTime, true);
                $totalDays += $diffDay;
            }

            if ($completeWorkOrderCount && $totalDays) {
                $itemTotal = round($completeWorkOrderCount / $totalDays, 2);
            } else {
                $itemTotal = 0;
            }
            $data[] = $itemTotal;
        }
        return $data;
    }

    private function getPersonEfficiencyMonthSelect($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';

        $builder = WorkOrder::query()
            ->select('work_order.*')
            ->join('customer', 'customer.id', '=', 'work_order.customer_id')
            ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE)
            ->where('work_order.create_time', '>=', strtotime($start))
            ->where('work_order.create_time', '<=', strtotime($end));

        $workOrderType = trim(request('work_order_type', ''));
        $workOrderTypes = WorkOrderRepo::types();
        if (!in_array($workOrderType, $workOrderTypes)) {
            $workOrderType = '';
        }
        $deptId = intval(request('dept_id', ''));

        if ($workOrderType) {
            $builder->where('work_order.type', $workOrderType);
        }
        if ($deptId) {
            $builder->where('customer.department_id', $deptId);
        }
        $workOrderData = $builder->get();

        foreach ($xAxis as $item) {
            $itemStart = Carbon::parse($item)->startOfMonth()->timestamp;
            $itemEnd = Carbon::parse($item)->endOfMonth()->timestamp;
            $completeWorkOrder = $workOrderData->filter(function ($item) use ($itemStart, $itemEnd) {
                return strtotime($item->create_time) >= $itemStart && strtotime($item->create_time) <= $itemEnd;
            });

            $completeWorkOrderCount = $completeWorkOrder->count();
            $completeIds = $completeWorkOrder->pluck('id')->toArray();
            $completeHistoryData = WorkOrder\StatusHistory::query()
                ->whereIn('work_order_id', $completeIds)
                ->get()
                ->groupBy('status')
                ->toArray();
            $createdData = $completeHistoryData[WorkOrderRepo::STATUS_CREATED] ?? [];
            $completeData = $completeHistoryData[WorkOrderRepo::STATUS_COMPLETE] ?? [];
            $completeData = collect($completeData)->keyBy('work_order_id') ->toArray();
            $totalDays = 0;
            foreach ($createdData as $createdItem) {
                $createdWorkOrderId = $createdItem['work_order_id'];
                $tempCompleteWorkOrder = $completeData[$createdWorkOrderId] ?? [];
                if (!$tempCompleteWorkOrder) {
                    continue;
                }
                $tempStartTime = $createdItem['update_time_format'];
                $tempCompleteTime = $tempCompleteWorkOrder['update_time_format'];
                $diffDay = Carbon::parse($tempStartTime)->diffInDays($tempCompleteTime, true);
                $totalDays += $diffDay;
            }
            if ($completeWorkOrderCount && $totalDays) {
                $data[] = round($completeWorkOrderCount / $totalDays, 2);
            } else {
                $data[] = 0;
            }
        }
        return $data;
    }

    // 及时关单率
    public function closeProbability($type = self::DATE_TYPE_MONTH_SELECT, $monthStart = '', $monthEnd = '')
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        if ($type == self::DATE_TYPE_MONTH_SELECT) {
            $data = $this->getCloseProbabilityMonthSelect($type, $monthStart, $monthEnd);
            return [
                'type' => $type,
                'month_start' => $monthStart,
                'month_end' => $monthEnd,
                'labels' => $xAxis,
                'data' => $data
            ];
        }
        if ($type == self::DATE_TYPE_WEEK) {
            $data = $this->getCloseProbabilityWeek($type, $monthStart, $monthEnd);
            return [
                'type' => $type,
                'month_start' => $monthStart,
                'month_end' => $monthEnd,
                'labels' => $xAxis,
                'data' => $data
            ];
        }
        if ($type == self::DATE_TYPE_MONTH) {
            $data = $this->getCloseProbabilityMonth($type, $monthStart, $monthEnd);
            return [
                'type' => $type,
                'month_start' => $monthStart,
                'month_end' => $monthEnd,
                'labels' => $xAxis,
                'data' => $data
            ];
        }
        if ($type == self::DATE_TYPE_QUARTER) {
            $data = $this->getCloseProbabilityQuarter($type, $monthStart, $monthEnd);
            return [
                'type' => $type,
                'month_start' => $monthStart,
                'month_end' => $monthEnd,
                'labels' => $xAxis,
                'data' => $data
            ];
        }
        if ($type == self::DATE_TYPE_YEAR) {
            $data = $this->getCloseProbabilityYear($type, $monthStart, $monthEnd);
            return [
                'type' => $type,
                'month_start' => $monthStart,
                'month_end' => $monthEnd,
                'labels' => $xAxis,
                'data' => $data
            ];
        }
    }

    private function getCloseProbabilityYear($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $builder = WorkOrder::query()
            ->where('update_time', '>=', strtotime($start))
            ->where('update_time', '<=', strtotime($end));
        $workOrderData = $builder->get();
        $data = [];
        foreach ($xAxis as $item) {
            $one = $two = $three = $four = 0;
            $itemStart = Carbon::parse($item)->startOfMonth()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfMonth()->toDateString() . ' 23:59:59';
            $itemTotal = $workOrderData
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd)
                ->count();
            $completeWorkOrder = $workOrderData
                ->where('status', WorkOrderRepo::STATUS_COMPLETE)
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd);
            $completeIds = $completeWorkOrder->pluck('id')->toArray();
            $completeHistoryData = WorkOrder\StatusHistory::query()
                ->whereIn('work_order_id', $completeIds)
                ->get()
                ->groupBy('status')
                ->toArray();
            $createdData = $completeHistoryData[WorkOrderRepo::STATUS_CREATED] ?? [];
            $completeData = $completeHistoryData[WorkOrderRepo::STATUS_COMPLETE] ?? [];
            $completeData = collect($completeData)->keyBy('work_order_id') ->toArray();
            foreach ($createdData as $createdItem) {
                $createdWorkOrderId = $createdItem['work_order_id'];
                $tempCompleteWorkOrder = $completeData[$createdWorkOrderId] ?? [];
                if (!$tempCompleteWorkOrder) {
                    continue;
                }
                $tempStartTime = $createdItem['update_time_format'];
                $tempCompleteTime = $tempCompleteWorkOrder['update_time_format'];
                $diffDay = Carbon::parse($tempStartTime)->diffInDays($tempCompleteTime, true);
                if ($diffDay <= 1) {
                    $one += 1;
                } elseif ($diffDay <= 2) {
                    $two += 1;
                } elseif ($diffDay <= 3) {
                    $three += 1;
                } elseif ($diffDay <= 4) {
                    $four += 1;
                }
            }
            if ($one && $itemTotal) {
                $one = round(round($one / $itemTotal, 4) * 100, 2);
            }
            if ($two && $itemTotal) {
                $two = round(round($two / $itemTotal, 4) * 100, 2);
            }
            if ($three && $itemTotal) {
                $three = round(round($three / $itemTotal, 4) * 100, 2);
            }
            if ($four && $itemTotal) {
                $four = round(round($four / $itemTotal, 4) * 100, 2);
            }
            $data[] = [
                'total' => $itemTotal,
                'one' => $one,
                'two' => $two,
                'three' => $three,
                'four' => $four
            ];
        }
        return $data;
    }

    private function getCloseProbabilityQuarter($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $builder = WorkOrder::query()
            ->where('update_time', '>=', strtotime($start))
            ->where('update_time', '<=', strtotime($end));
        $workOrderData = $builder->get();
        $data = [];
        foreach ($xAxis as $item) {
            $one = $two = $three = $four = 0;
            $itemStart = Carbon::parse($item)->startOfMonth()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfMonth()->toDateString() . ' 23:59:59';
            $itemTotal = $workOrderData
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd)
                ->count();
            $completeWorkOrder = $workOrderData
                ->where('status', WorkOrderRepo::STATUS_COMPLETE)
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd);
            $completeIds = $completeWorkOrder->pluck('id')->toArray();
            $completeHistoryData = WorkOrder\StatusHistory::query()
                ->whereIn('work_order_id', $completeIds)
                ->get()
                ->groupBy('status')
                ->toArray();
            $createdData = $completeHistoryData[WorkOrderRepo::STATUS_CREATED] ?? [];
            $completeData = $completeHistoryData[WorkOrderRepo::STATUS_COMPLETE] ?? [];
            $completeData = collect($completeData)->keyBy('work_order_id') ->toArray();
            foreach ($createdData as $createdItem) {
                $createdWorkOrderId = $createdItem['work_order_id'];
                $tempCompleteWorkOrder = $completeData[$createdWorkOrderId] ?? [];
                if (!$tempCompleteWorkOrder) {
                    continue;
                }
                $tempStartTime = $createdItem['update_time_format'];
                $tempCompleteTime = $tempCompleteWorkOrder['update_time_format'];
                $diffDay = Carbon::parse($tempStartTime)->diffInDays($tempCompleteTime, true);
                if ($diffDay <= 1) {
                    $one += 1;
                } elseif ($diffDay <= 2) {
                    $two += 1;
                } elseif ($diffDay <= 3) {
                    $three += 1;
                } elseif ($diffDay <= 4) {
                    $four += 1;
                }
            }
            if ($one && $itemTotal) {
                $one = round(round($one / $itemTotal, 4) * 100, 2);
            }
            if ($two && $itemTotal) {
                $two = round(round($two / $itemTotal, 4) * 100, 2);
            }
            if ($three && $itemTotal) {
                $three = round(round($three / $itemTotal, 4) * 100, 2);
            }
            if ($four && $itemTotal) {
                $four = round(round($four / $itemTotal, 4) * 100, 2);
            }
            $data[] = [
                'total' => $itemTotal,
                'one' => $one,
                'two' => $two,
                'three' => $three,
                'four' => $four
            ];
        }
        return $data;
    }

    private function getCloseProbabilityMonth($type, $monthStart, $monthEnd)
    {
        $start = Carbon::now()->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::now()->endOfMonth()->toDateString() . ' 23:59:59';
        $weeks = [
            Carbon::parse($start)->toDateString(),
            Carbon::parse($start)->addDays(7)->toDateString(),
            Carbon::parse($start)->addDays(14)->toDateString(),
            Carbon::parse($start)->addDays(21)->toDateString()
        ];
        $builder = WorkOrder::query()
            ->where('update_time', '>=', strtotime($start))
            ->where('update_time', '<=', strtotime($end));
        $workOrderData = $builder->get();
        $data = [];
        foreach ($weeks as $item) {
            $one = $two = $three = $four = 0;
            $itemStart = Carbon::parse($item)->startOfWeek()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfWeek()->toDateString() . ' 23:59:59';
            $itemTotal = $workOrderData
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd)
                ->count();
            $completeWorkOrder = $workOrderData
                ->where('status', WorkOrderRepo::STATUS_COMPLETE)
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd);
            $completeIds = $completeWorkOrder->pluck('id')->toArray();
            $completeHistoryData = WorkOrder\StatusHistory::query()
                ->whereIn('work_order_id', $completeIds)
                ->get()
                ->groupBy('status')
                ->toArray();
            $createdData = $completeHistoryData[WorkOrderRepo::STATUS_CREATED] ?? [];
            $completeData = $completeHistoryData[WorkOrderRepo::STATUS_COMPLETE] ?? [];
            $completeData = collect($completeData)->keyBy('work_order_id') ->toArray();
            foreach ($createdData as $createdItem) {
                $createdWorkOrderId = $createdItem['work_order_id'];
                $tempCompleteWorkOrder = $completeData[$createdWorkOrderId] ?? [];
                if (!$tempCompleteWorkOrder) {
                    continue;
                }
                $tempStartTime = $createdItem['update_time_format'];
                $tempCompleteTime = $tempCompleteWorkOrder['update_time_format'];
                $diffDay = Carbon::parse($tempStartTime)->diffInDays($tempCompleteTime, true);
                if ($diffDay <= 1) {
                    $one += 1;
                } elseif ($diffDay <= 2) {
                    $two += 1;
                } elseif ($diffDay <= 3) {
                    $three += 1;
                } elseif ($diffDay <= 4) {
                    $four += 1;
                }
            }
            if ($one && $itemTotal) {
                $one = round(round($one / $itemTotal, 4) * 100, 2);
            }
            if ($two && $itemTotal) {
                $two = round(round($two / $itemTotal, 4) * 100, 2);
            }
            if ($three && $itemTotal) {
                $three = round(round($three / $itemTotal, 4) * 100, 2);
            }
            if ($four && $itemTotal) {
                $four = round(round($four / $itemTotal, 4) * 100, 2);
            }
            $data[] = [
                'total' => $itemTotal,
                'one' => $one,
                'two' => $two,
                'three' => $three,
                'four' => $four
            ];
        }
        return $data;
    }

    private function getCloseProbabilityWeek($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = $xAxis[0] . ' 00:00:00';
        $end = $xAxis[count($xAxis) - 1] . ' 23:59:59';
        $builder = WorkOrder::query()
            ->where('update_time', '>=', strtotime($start))
            ->where('update_time', '<=', strtotime($end));
        $workOrderData = $builder->get();
        $data = [];
        foreach ($xAxis as $item) {
            $one = $two = $three = $four = 0;
            $itemStart = Carbon::parse($item)->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->toDateString() . ' 23:59:59';
            $itemTotal = $workOrderData
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd)
                ->count();
            $completeWorkOrder = $workOrderData
                ->where('status', WorkOrderRepo::STATUS_COMPLETE)
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd);
            $completeIds = $completeWorkOrder->pluck('id')->toArray();
            $completeHistoryData = WorkOrder\StatusHistory::query()
                ->whereIn('work_order_id', $completeIds)
                ->get()
                ->groupBy('status')
                ->toArray();
            $createdData = $completeHistoryData[WorkOrderRepo::STATUS_CREATED] ?? [];
            $completeData = $completeHistoryData[WorkOrderRepo::STATUS_COMPLETE] ?? [];
            $completeData = collect($completeData)->keyBy('work_order_id') ->toArray();
            foreach ($createdData as $createdItem) {
                $createdWorkOrderId = $createdItem['work_order_id'];
                $tempCompleteWorkOrder = $completeData[$createdWorkOrderId] ?? [];
                if (!$tempCompleteWorkOrder) {
                    continue;
                }
                $tempStartTime = $createdItem['update_time_format'];
                $tempCompleteTime = $tempCompleteWorkOrder['update_time_format'];
                $diffDay = Carbon::parse($tempStartTime)->diffInDays($tempCompleteTime, true);
                if ($diffDay <= 1) {
                    $one += 1;
                } elseif ($diffDay <= 2) {
                    $two += 1;
                } elseif ($diffDay <= 3) {
                    $three += 1;
                } elseif ($diffDay <= 4) {
                    $four += 1;
                }
            }
            if ($one && $itemTotal) {
                $one = round(round($one / $itemTotal, 4) * 100, 2);
            }
            if ($two && $itemTotal) {
                $two = round(round($two / $itemTotal, 4) * 100, 2);
            }
            if ($three && $itemTotal) {
                $three = round(round($three / $itemTotal, 4) * 100, 2);
            }
            if ($four && $itemTotal) {
                $four = round(round($four / $itemTotal, 4) * 100, 2);
            }
            $data[] = [
                'total' => $itemTotal,
                'one' => $one,
                'two' => $two,
                'three' => $three,
                'four' => $four
            ];
        }
        return $data;
    }

    private function getCloseProbabilityMonthSelect($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $builder = WorkOrder::query()
            ->where('update_time', '>=', strtotime($start))
            ->where('update_time', '<=', strtotime($end));
        $workOrderData = $builder->get();
        $data = [];
        foreach ($xAxis as $item) {
            $one = $two = $three = $four = 0;
            $itemStart = Carbon::parse($item)->startOfMonth() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfMonth() . ' 23:59:59';
            $itemTotal = $workOrderData
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd)
                ->count();
            $completeWorkOrder = $workOrderData
                ->where('status', WorkOrderRepo::STATUS_COMPLETE)
                ->where('update_time', '>=', $itemStart)
                ->where('update_time', '<=', $itemEnd);
            $completeIds = $completeWorkOrder->pluck('id')->toArray();
            $completeHistoryData = WorkOrder\StatusHistory::query()
                ->whereIn('work_order_id', $completeIds)
                ->get()
                ->groupBy('status')
                ->toArray();
            $createdData = $completeHistoryData[WorkOrderRepo::STATUS_CREATED] ?? [];
            $completeData = $completeHistoryData[WorkOrderRepo::STATUS_COMPLETE] ?? [];
            $completeData = collect($completeData)->keyBy('work_order_id') ->toArray();
            foreach ($createdData as $createdItem) {
                $createdWorkOrderId = $createdItem['work_order_id'];
                $tempCompleteWorkOrder = $completeData[$createdWorkOrderId] ?? [];
                if (!$tempCompleteWorkOrder) {
                    continue;
                }
                $tempStartTime = $createdItem['update_time_format'];
                $tempCompleteTime = $tempCompleteWorkOrder['update_time_format'];
                $diffDay = Carbon::parse($tempStartTime)->diffInDays($tempCompleteTime, true);
                if ($diffDay <= 1) {
                    $one += 1;
                } elseif ($diffDay <= 2) {
                    $two += 1;
                } elseif ($diffDay <= 3) {
                    $three += 1;
                } elseif ($diffDay <= 4) {
                    $four += 1;
                }
            }
            if ($one && $itemTotal) {
                $one = round(round($one / $itemTotal, 4) * 100, 2);
            }
            if ($two && $itemTotal) {
                $two = round(round($two / $itemTotal, 4) * 100, 2);
            }
            if ($three && $itemTotal) {
                $three = round(round($three / $itemTotal, 4) * 100, 2);
            }
            if ($four && $itemTotal) {
                $four = round(round($four / $itemTotal, 4) * 100, 2);
            }
            $data[] = [
                'total' => $itemTotal,
                'one' => $one,
                'two' => $two,
                'three' => $three,
                'four' => $four
            ];
        }
        return $data;
    }

    /**
     * 平均服务用时
     * 工单创建到完成的时间的所有服务时间之和 ÷ 统计期间内工单的完成数量之和
     * @param string $type
     * @param string $monthStart
     * @param string $montEnd
     */
    public function avgServiceTime($type = self::DATE_TYPE_MONTH_SELECT, $monthStart = '', $monthEnd = '')
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        if ($type == self::DATE_TYPE_MONTH_SELECT) {
            $data = $this->getAvgServiceTimeMonthSelect($type, $monthStart, $monthEnd);
        }
        if ($type == self::DATE_TYPE_WEEK) {
            $data = $this->getAvgServiceTimeWeek($type, $monthStart, $monthEnd);
        }
        if ($type == self::DATE_TYPE_MONTH) {
            $data = $this->getAvgServiceTimeMonth($type, $monthStart, $monthEnd);
        }
        if ($type == self::DATE_TYPE_QUARTER) {
            $data = $this->getAvgServiceTimeQuarter($type, $monthStart, $monthEnd);
        }
        if ($type == self::DATE_TYPE_YEAR) {
            $data = $this->getAvgServiceTimeYear($type, $monthStart, $monthEnd);
        }

        return [
            'type' => $type,
            'month_start' => $monthStart,
            'month_end' => $monthEnd,
            'data' => [
                'labels' => $xAxis,
                'data' => $data
            ]
        ];
    }

    private function getAvgServiceTimeYear($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $builder = WorkOrder::query()
            ->select(['work_order.id', 'work_order.update_time'])
            ->join('customer', 'customer.id', '=', 'work_order.customer_id')
            ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE)
            ->where('work_order.update_time', '>=', strtotime($start))
            ->where('work_order.update_time', '<=', strtotime($end));

        $workOrderType = trim(request('work_order_type', ''));
        $workOrderTypes = WorkOrderRepo::types();
        if (!in_array($workOrderType, $workOrderTypes)) {
            $workOrderType = '';
        }
        $deptId = intval(request('dept_id', ''));

        if ($workOrderType) {
            $builder->where('work_order.type', $workOrderType);
        }
        if ($deptId) {
            $builder->where('customer.department_id', $deptId);
        }

        $completeIds = $builder->get()->toArray();
        $data = [];
        foreach ($xAxis as $item) {
            $itemStart = Carbon::parse($item)->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->toDateString() . ' 23:59:59';
            $woIds = [];

            $sumTime = $countWo = 0;

            foreach ($completeIds as $completeId) {
                $completeItemId = $completeId['id'];
                $completeItemUpdateTime = $completeId['update_time'];
                if (!is_numeric($completeItemUpdateTime)) {
                    $completeItemUpdateTime = strtotime($completeItemUpdateTime);
                }
                if ($completeItemUpdateTime < strtotime($itemStart) || $completeItemUpdateTime > strtotime($itemEnd)) {
                    continue;
                }
                $countWo += 1;
                $woIds[] = $completeItemId;
            }
            if ($woIds) {
                foreach ($woIds as $woId) {
                    $timesBuilder = WorkOrder\StatusHistory::query()
                        ->where('work_order_id', $woId)
                        ->where(function ($query) {
                            return $query->where('status', WorkOrderRepo::STATUS_CREATED)
                                ->orWhere('status', WorkOrderRepo::STATUS_COMPLETE);
                        });
                    $timesData = $timesBuilder->get()->keyBy('status')->toArray();
                    $timesKeys = array_keys($timesData);
                    // 没有开始时间或者没有完成时间，则视为脏数据，跳过
                    if (!in_array(WorkOrderRepo::STATUS_COMPLETE, $timesKeys) || !in_array(WorkOrderRepo::STATUS_CREATED, $timesKeys)) {
                        continue;
                    }
                    $woStartTime = $timesData[WorkOrderRepo::STATUS_CREATED]['update_time_format'];
                    $woCompleteTime = $timesData[WorkOrderRepo::STATUS_COMPLETE]['update_time_format'];
                    if ($woCompleteTime < $itemStart) {
                        continue;
                    }
                    $diffHours = Carbon::parse($woStartTime)->diffInHours($woCompleteTime, true);
                    $sumTime += $diffHours;
                }
            }
            if ($sumTime && $countWo) {
                $data[] = round($sumTime / $countWo, 2);
            } else {
                $data[] = 0;
            }
        }
        return $data;
    }

    private function getAvgServiceTimeQuarter($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $builder = WorkOrder::query()
            ->select(['work_order.id', 'work_order.update_time'])
            ->join('customer', 'customer.id', '=', 'work_order.customer_id')
            ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE)
            ->where('work_order.update_time', '>=', strtotime($start))
            ->where('work_order.update_time', '<=', strtotime($end));

        $workOrderType = trim(request('work_order_type', ''));
        $workOrderTypes = WorkOrderRepo::types();
        if (!in_array($workOrderType, $workOrderTypes)) {
            $workOrderType = '';
        }
        $deptId = intval(request('dept_id', ''));

        if ($workOrderType) {
            $builder->where('work_order.type', $workOrderType);
        }
        if ($deptId) {
            $builder->where('customer.department_id', $deptId);
        }

        $completeIds = $builder->get()->toArray();
        $data = [];
        foreach ($xAxis as $item) {
            $itemStart = Carbon::parse($item)->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->toDateString() . ' 23:59:59';
            $woIds = [];

            $sumTime = $countWo = 0;

            foreach ($completeIds as $completeId) {
                $completeItemId = $completeId['id'];
                $completeItemUpdateTime = $completeId['update_time'];
                if (!is_numeric($completeItemUpdateTime)) {
                    $completeItemUpdateTime = strtotime($completeItemUpdateTime);
                }
                if ($completeItemUpdateTime < strtotime($itemStart) || $completeItemUpdateTime > strtotime($itemEnd)) {
                    continue;
                }
                $countWo += 1;
                $woIds[] = $completeItemId;
            }
            if ($woIds) {
                foreach ($woIds as $woId) {
                    $timesBuilder = WorkOrder\StatusHistory::query()
                        ->where('work_order_id', $woId)
                        ->where(function ($query) {
                            return $query->where('status', WorkOrderRepo::STATUS_CREATED)
                                ->orWhere('status', WorkOrderRepo::STATUS_COMPLETE);
                        });
                    $timesData = $timesBuilder->get()->keyBy('status')->toArray();
                    $timesKeys = array_keys($timesData);
                    // 没有开始时间或者没有完成时间，则视为脏数据，跳过
                    if (!in_array(WorkOrderRepo::STATUS_COMPLETE, $timesKeys) || !in_array(WorkOrderRepo::STATUS_CREATED, $timesKeys)) {
                        continue;
                    }
                    $woStartTime = $timesData[WorkOrderRepo::STATUS_CREATED]['update_time_format'];
                    $woCompleteTime = $timesData[WorkOrderRepo::STATUS_COMPLETE]['update_time_format'];
                    if ($woCompleteTime < $itemStart) {
                        continue;
                    }
                    $diffHours = Carbon::parse($woStartTime)->diffInHours($woCompleteTime, true);
                    $sumTime += $diffHours;
                }
            }
            if ($sumTime && $countWo) {
                $data[] = round($sumTime / $countWo, 2);
            } else {
                $data[] = 0;
            }
        }
        return $data;
    }

    private function getAvgServiceTimeMonth($type, $monthStart, $monthEnd)
    {
        $start = Carbon::now()->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::now()->endOfMonth()->toDateString() . ' 23:59:59';
        $weeks = [
            Carbon::parse($start)->toDateString(),
            Carbon::parse($start)->addDays(7)->toDateString(),
            Carbon::parse($start)->addDays(14)->toDateString(),
            Carbon::parse($start)->addDays(21)->toDateString()
        ];
        $builder = WorkOrder::query()
            ->select(['work_order.id', 'work_order.update_time'])
            ->join('customer', 'customer.id', '=', 'work_order.customer_id')
            ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE)
            ->where('work_order.update_time', '>=', strtotime($start))
            ->where('work_order.update_time', '<=', strtotime($end));

        $workOrderType = trim(request('work_order_type', ''));
        $workOrderTypes = WorkOrderRepo::types();
        if (!in_array($workOrderType, $workOrderTypes)) {
            $workOrderType = '';
        }
        $deptId = intval(request('dept_id', ''));

        if ($workOrderType) {
            $builder->where('work_order.type', $workOrderType);
        }
        if ($deptId) {
            $builder->where('customer.department_id', $deptId);
        }

        $completeIds = $builder->get()->toArray();
        $data = [];
        foreach ($weeks as $item) {
            $itemStart = Carbon::parse($item)->startOfWeek()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfWeek()->toDateString() . ' 23:59:59';
            $woIds = [];

            $sumTime = $countWo = 0;

            foreach ($completeIds as $completeId) {
                $completeItemId = $completeId['id'];
                $completeItemUpdateTime = $completeId['update_time'];
                if (!is_numeric($completeItemUpdateTime)) {
                    $completeItemUpdateTime = strtotime($completeItemUpdateTime);
                }
                if ($completeItemUpdateTime < strtotime($itemStart) || $completeItemUpdateTime > strtotime($itemEnd)) {
                    continue;
                }
                $countWo += 1;
                $woIds[] = $completeItemId;
            }
            if ($woIds) {
                foreach ($woIds as $woId) {
                    $timesBuilder = WorkOrder\StatusHistory::query()
                        ->where('work_order_id', $woId)
                        ->where(function ($query) {
                            return $query->where('status', WorkOrderRepo::STATUS_CREATED)
                                ->orWhere('status', WorkOrderRepo::STATUS_COMPLETE);
                        });
                    $timesData = $timesBuilder->get()->keyBy('status')->toArray();
                    $timesKeys = array_keys($timesData);
                    // 没有开始时间或者没有完成时间，则视为脏数据，跳过
                    if (!in_array(WorkOrderRepo::STATUS_COMPLETE, $timesKeys) || !in_array(WorkOrderRepo::STATUS_CREATED, $timesKeys)) {
                        continue;
                    }
                    $woStartTime = $timesData[WorkOrderRepo::STATUS_CREATED]['update_time_format'];
                    $woCompleteTime = $timesData[WorkOrderRepo::STATUS_COMPLETE]['update_time_format'];
                    if ($woCompleteTime < $itemStart) {
                        continue;
                    }
                    $diffHours = Carbon::parse($woStartTime)->diffInHours($woCompleteTime, true);
                    $sumTime += $diffHours;
                }
            }
            if ($sumTime && $countWo) {
                $data[] = round($sumTime / $countWo, 2);
            } else {
                $data[] = 0;
            }
        }
        return $data;
    }

    private function getAvgServiceTimeWeek($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = $xAxis[0] . ' 00:00:00';
        $end = $xAxis[count($xAxis) - 1] . ' 23:59:59';
        $builder = WorkOrder::query()
            ->select(['work_order.id', 'work_order.update_time'])
            ->join('customer', 'customer.id', '=', 'work_order.customer_id')
            ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE)
            ->where('work_order.update_time', '>=', strtotime($start))
            ->where('work_order.update_time', '<=', strtotime($end));

        $workOrderType = trim(request('work_order_type', ''));
        $workOrderTypes = WorkOrderRepo::types();
        if (!in_array($workOrderType, $workOrderTypes)) {
            $workOrderType = '';
        }
        $deptId = intval(request('dept_id', ''));

        if ($workOrderType) {
            $builder->where('work_order.type', $workOrderType);
        }
        if ($deptId) {
            $builder->where('customer.department_id', $deptId);
        }

        $completeIds = $builder->get()->toArray();
        $data = [];
        foreach ($xAxis as $item) {
            $itemStart = Carbon::parse($item)->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->toDateString() . ' 23:59:59';
            $woIds = [];

            $sumTime = $countWo = 0;

            foreach ($completeIds as $completeId) {
                $completeItemId = $completeId['id'];
                $completeItemUpdateTime = $completeId['update_time'];
                if (!is_numeric($completeItemUpdateTime)) {
                    $completeItemUpdateTime = strtotime($completeItemUpdateTime);
                }
                if ($completeItemUpdateTime < strtotime($itemStart) || $completeItemUpdateTime > strtotime($itemEnd)) {
                    continue;
                }
                $countWo += 1;
                $woIds[] = $completeItemId;
            }
            if ($woIds) {
                foreach ($woIds as $woId) {
                    $timesBuilder = WorkOrder\StatusHistory::query()
                        ->where('work_order_id', $woId)
                        ->where(function ($query) {
                            return $query->where('status', WorkOrderRepo::STATUS_CREATED)
                                ->orWhere('status', WorkOrderRepo::STATUS_COMPLETE);
                        });
                    $timesData = $timesBuilder->get()->keyBy('status')->toArray();
                    $timesKeys = array_keys($timesData);
                    // 没有开始时间或者没有完成时间，则视为脏数据，跳过
                    if (!in_array(WorkOrderRepo::STATUS_COMPLETE, $timesKeys) || !in_array(WorkOrderRepo::STATUS_CREATED, $timesKeys)) {
                        continue;
                    }
                    $woStartTime = $timesData[WorkOrderRepo::STATUS_CREATED]['update_time_format'];
                    $woCompleteTime = $timesData[WorkOrderRepo::STATUS_COMPLETE]['update_time_format'];
                    if ($woCompleteTime < $itemStart) {
                        continue;
                    }
                    $diffHours = Carbon::parse($woStartTime)->diffInHours($woCompleteTime, true);
                    $sumTime += $diffHours;
                }
            }
            if ($sumTime && $countWo) {
                $data[] = round($sumTime / $countWo, 2);
            } else {
                $data[] = 0;
            }
        }
        return $data;
    }

    private function getAvgServiceTimeMonthSelect($type, $monthStart, $monthEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $builder = WorkOrder::query()
            ->select(['work_order.id', 'work_order.update_time'])
            ->join('customer', 'customer.id', '=', 'work_order.customer_id')
            ->where('work_order.status', WorkOrderRepo::STATUS_COMPLETE);
        $builder->where('work_order.update_time', '>=', strtotime($start))
            ->where('work_order.update_time', '<=', strtotime($end));

        $workOrderType = trim(request('work_order_type', ''));
        $workOrderTypes = WorkOrderRepo::types();
        if (!in_array($workOrderType, $workOrderTypes)) {
            $workOrderType = '';
        }
        $deptId = intval(request('dept_id', ''));

        if ($workOrderType) {
            $builder->where('work_order.type', $workOrderType);
        }
        if ($deptId) {
            $builder->where('customer.department_id', $deptId);
        }

        $completeIds = $builder->get()->toArray();
        // 工单创建到完成的时间的所有服务时间之和 ÷ 统计期间内工单的完成数量之和
        $data = [];
        foreach ($xAxis as $item) {
            $itemStart = Carbon::parse($item)->startOfMonth()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfMonth()->toDateString() . ' 23:59:59';

            $woIds = [];

            $sumTime = $countWo = 0;

            foreach ($completeIds as $completeId) {
                $completeItemId = $completeId['id'];
                $completeItemUpdateTime = $completeId['update_time'];
                if (!is_numeric($completeItemUpdateTime)) {
                    $completeItemUpdateTime = strtotime($completeItemUpdateTime);
                }
                if ($completeItemUpdateTime < strtotime($itemStart) || $completeItemUpdateTime > strtotime($itemEnd)) {
                    continue;
                }
                $countWo += 1;
                $woIds[] = $completeItemId;
            }
            if ($woIds) {
                foreach ($woIds as $woId) {
                    $timesBuilder = WorkOrder\StatusHistory::query()
                        ->where('work_order_id', $woId)
                        ->where(function ($query) {
                            return $query->where('status', WorkOrderRepo::STATUS_CREATED)
                                ->orWhere('status', WorkOrderRepo::STATUS_COMPLETE);
                        });
                    $timesData = $timesBuilder->get()->keyBy('status')->toArray();
                    $timesKeys = array_keys($timesData);
                    // 没有开始时间或者没有完成时间，则视为脏数据，跳过
                    if (!in_array(WorkOrderRepo::STATUS_COMPLETE, $timesKeys) || !in_array(WorkOrderRepo::STATUS_CREATED, $timesKeys)) {
                        continue;
                    }
                    $woStartTime = $timesData[WorkOrderRepo::STATUS_CREATED]['update_time_format'];
                    $woCompleteTime = $timesData[WorkOrderRepo::STATUS_COMPLETE]['update_time_format'];
                    $diffHours = Carbon::parse($woStartTime)->diffInHours($woCompleteTime, true);
                    $sumTime += $diffHours;
                }
            }
            if ($sumTime && $countWo) {
                $data[] = round($sumTime / $countWo, 2);
            } else {
                $data[] = 0;
            }
        }
        return $data;
    }

    /**
     * 满意度好评率
     * 时间:
     *  type: 本周(week)、本月(month)、本季度(quarter)、本年(year)、按月选择(month_select)
     *  type = month_select: -> month_start, month_end
     *  week: 7天数据, 本月: 四周数据, 本季度: 三个月数据, 本年: 12个月数据
     *  month_select: month_start - 1 -> month_end
     *  默认: 按月选择(month_select)
     */
    public function satisfactionProbability($type = self::DATE_TYPE_MONTH_SELECT, $monthStart = '', $montEnd = '')
    {
        $xAxis = $this->getXAxis($type, $monthStart, $montEnd);
        $data = $this->getSatisfactionProbabilityData($type, $monthStart, $montEnd);
        return [
            'type' => $type,
            'month_start' => $monthStart,
            'month_end' => $montEnd,
            'labels' => $xAxis,
            'data' => $data
        ];
    }

    // 客户满意度构成
    public function satisfactionForm($type = self::DATE_TYPE_MONTH_SELECT, $monthStart = '', $monthEnd = '')
    {
        $xAxis = ['非常满意', '满意', '一般', '不满意', '非常不满意'];
        $data = $this->getSatisfactionFormData($type, $monthStart, $monthEnd);
        return [
            'type' => $type,
            'month_start' => $monthStart,
            'month_end' => $monthEnd,
            'data' => [
                'labels' => $xAxis,
                'data' => [
                    $data['level_5'],
                    $data['level_4'],
                    $data['level_3'],
                    $data['level_2'],
                    $data['level_1']
                ]
            ]
        ];
    }

    // 客户满意度构成 数据
    public function getSatisfactionFormData($type, $monthStart = '', $monthEnd = '')
    {
        $builder = WorkOrder\Receipt::query()
            ->select([DB::raw('count(*) as count'), 'satisfaction'])
            ->groupBy('satisfaction');
        $start = $end = '';
        if ($type == self::DATE_TYPE_MONTH_SELECT) {
            $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
            $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
            $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        }
        if ($type == self::DATE_TYPE_WEEK) {
            $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
            $start = $xAxis[0] . ' 00:00:00';
            $end = $xAxis[count($xAxis) - 1] . ' 23:59:59';
        }
        if ($type == self::DATE_TYPE_MONTH) {
            $start = Carbon::now()->startOfMonth()->toDateString() . ' 00:00:00';
            $end = Carbon::now()->endOfMonth()->toDateString() . ' 23:59:59';
        }
        if ($type == self::DATE_TYPE_QUARTER) {
            $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
            $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
            $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        }
        if ($type == self::DATE_TYPE_YEAR) {
            $xAxis = $this->getXAxis($type, $monthStart, $monthEnd);
            $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
            $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        }
        if (!$start || !$end) {
            return [];
        }
        $builder->where('create_time', '>=', strtotime($start))
            ->where('create_time', '<=', strtotime($end));
        $formData = $builder->get()->keyBy('satisfaction')->toArray();
        $level1 = $level2 = $level3 = $level4 = $level5 = 0;
        foreach ($formData as $itemSatisfaction => $item) {
            if ($itemSatisfaction < 2) {
                $level1 += $item['count'];
            } elseif ($itemSatisfaction == 2) {
                $level2 += $item['count'];
            } elseif ($itemSatisfaction == 3) {
                $level3 += $item['count'];
            } elseif ($itemSatisfaction == 4) {
                $level4 += $item['count'];
            } else {
                $level5 += $item['count'];
            }
        }

        return [
            'level_1' => $level1,
            'level_2' => $level2,
            'level_3' => $level3,
            'level_4' => $level4,
            'level_5' => $level5
        ];
    }

    /**
     * 获取 满意度好评率数据
     *
     * @param $type
     * @param string $monthStart
     * @param string $montEnd
     */
    private function getSatisfactionProbabilityData($type, $monthStart = '', $montEnd = '')
    {
        if ($type == self::DATE_TYPE_MONTH_SELECT) {
            return self::getSatisfactionProbabilityMonthSelectData($type, $monthStart, $montEnd);
        }
        if ($type == self::DATE_TYPE_WEEK) {
            return self::getSatisfactionProbabilityWeekData($type, $monthStart, $montEnd);
        }
        if ($type == self::DATE_TYPE_MONTH) {
            return $this->getSatisfactionProbabilityMonthData($type, $monthStart, $montEnd);
        }
        if ($type == self::DATE_TYPE_QUARTER) {
            return $this->getSatisfactionProbabilityQuarterData($type, $monthStart, $montEnd);
        }
        if ($type == self::DATE_TYPE_YEAR) {
            return $this->getSatisfactionProbabilityYearData($type, $monthStart, $montEnd);
        }
        return [];
    }

    private function getSatisfactionProbabilityYearData($type, $monthStart, $montEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $montEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $dataDb = WorkOrder\Receipt::query()
            ->select(['satisfaction', 'create_time'])
            ->where('create_time', '>=', strtotime($start))
            ->where('create_time', '<=', strtotime($end))
            ->get()
            ->toArray();
        $data = [];
        foreach ($xAxis as $item) {
            $itemSatisfactionCount = $itemCount = 0;
            $itemStart = Carbon::parse($item)->startOfMonth()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfMonth()->toDateString() . ' 23:59:59';
            foreach ($dataDb as $itemDb) {
                $itemDbCreateTime = Carbon::parse($itemDb['create_time'])->toDateTimeString();
                if ($itemDbCreateTime >= $itemStart && $itemDbCreateTime <= $itemEnd) {
                    $itemCount += 1;
                    if ($itemDb['satisfaction'] >= 4) {
                        $itemSatisfactionCount += 1;
                    }
                }
            }
            $itemProbability = 0;
            if ($itemSatisfactionCount > 0 && $itemCount > 0) {
                $itemProbability = round($itemSatisfactionCount / $itemCount, 2);
            }
            $data[] = $itemProbability;
        }
        return $data;
    }

    private function getSatisfactionProbabilityQuarterData($type, $monthStart, $montEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $montEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $dataDb = WorkOrder\Receipt::query()
            ->select(['satisfaction', 'create_time'])
            ->where('create_time', '>=', strtotime($start))
            ->where('create_time', '<=', strtotime($end))
            ->get()
            ->toArray();
        $data = [];
        foreach ($xAxis as $item) {
            $itemSatisfactionCount = $itemCount = 0;
            $itemStart = Carbon::parse($item)->startOfMonth()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfMonth()->toDateString() . ' 23:59:59';
            foreach ($dataDb as $itemDb) {
                $itemDbCreateTime = Carbon::parse($itemDb['create_time'])->toDateTimeString();
                if ($itemDbCreateTime >= $itemStart && $itemDbCreateTime <= $itemEnd) {
                    $itemCount += 1;
                    if ($itemDb['satisfaction'] >= 4) {
                        $itemSatisfactionCount += 1;
                    }
                }
            }
            $itemProbability = 0;
            if ($itemSatisfactionCount > 0 && $itemCount > 0) {
                $itemProbability = round($itemSatisfactionCount / $itemCount, 2);
            }
            $data[] = $itemProbability;
        }
        return $data;
    }

    private function getSatisfactionProbabilityMonthData($type, $monthStart, $montEnd)
    {
        $start = Carbon::now()->startOfMonth()->toDateString();
        $end = Carbon::now()->endOfMonth()->toDateString();
        $weeks = [
            Carbon::parse($start)->toDateString(),
            Carbon::parse($start)->addDays(7)->toDateString(),
            Carbon::parse($start)->addDays(14)->toDateString(),
            Carbon::parse($start)->addDays(21)->toDateString()
        ];

        $dataDb = WorkOrder\Receipt::query()
            ->select(['satisfaction', 'create_time'])
            ->where('create_time', '>=', strtotime($start . ' 00:00:00'))
            ->where('create_time', '<=', strtotime($end . ' 23:59:59'))
            ->get()
            ->toArray();
        $data = [];
        foreach ($weeks as $item) {
            $itemStart = Carbon::parse($item)->startOfWeek()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfWeek()->toDateString() . ' 23:59:59';
            $itemSatisfactionCount = $itemCount = 0;
            foreach ($dataDb as $itemDb) {
                $itemDbCreateTime = Carbon::parse($itemDb['create_time'])->toDateTimeString();
                if ($itemDbCreateTime >= $itemStart && $itemDbCreateTime <= $itemEnd) {
                    $itemCount += 1;
                    if ($itemDb['satisfaction'] >= 4) {
                        $itemSatisfactionCount += 1;
                    }
                }
            }
            $itemProbability = 0;
            if ($itemSatisfactionCount > 0 && $itemCount > 0) {
                $itemProbability = round($itemSatisfactionCount / $itemCount, 2);
            }
            $data[] = $itemProbability;
        }
        return $data;
    }

    private function getSatisfactionProbabilityWeekData($type, $monthStart, $montEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $montEnd);
        $data = [];
        $start = $xAxis[0] . ' 00:00:00';
        $end = $xAxis[count($xAxis) - 1] . ' 23:59:59';
        $dataDb = WorkOrder\Receipt::query()
            ->select(['satisfaction', 'create_time'])
            ->where('create_time', '>=', strtotime($start))
            ->where('create_time', '<=', strtotime($end))
            ->get()
            ->toArray();
        foreach ($xAxis as $item) {
            $itemSatisfactionCount = $itemCount = 0;
            $itemStart = $item . ' 00:00:00';
            $itemEnd = $item . ' 23:59:59';
            foreach ($dataDb as $itemDb) {
                $itemDbCreateTime = Carbon::parse($itemDb['create_time'])->toDateTimeString();
                if ($itemDbCreateTime >= $itemStart && $itemDbCreateTime <= $itemEnd) {
                    $itemCount += 1;
                    if ($itemDb['satisfaction'] >= 4) {
                        $itemSatisfactionCount += 1;
                    }
                }
            }
            $itemProbability = 0;
            if ($itemSatisfactionCount > 0 && $itemCount > 0) {
                $itemProbability = round($itemSatisfactionCount / $itemCount, 2);
            }
            $data[] = $itemProbability;
        }
        return $data;
    }

    private function getSatisfactionProbabilityMonthSelectData($type, $monthStart, $montEnd)
    {
        $xAxis = $this->getXAxis($type, $monthStart, $montEnd);
        $start = Carbon::parse($xAxis[0])->startOfMonth()->toDateString() . ' 00:00:00';
        $end = Carbon::parse($xAxis[count($xAxis) - 1])->endOfMonth()->toDateString() . ' 23:59:59';
        $dataDb = WorkOrder\Receipt::query()
            ->select(['satisfaction', 'create_time'])
            ->where('create_time', '>=', strtotime($start))
            ->where('create_time', '<=', strtotime($end))
            ->get()
            ->toArray();
        $data = [];
        foreach ($xAxis as $item) {
            $itemSatisfactionCount = $itemCount = 0;
            $itemStart = Carbon::parse($item)->startOfMonth()->toDateString() . ' 00:00:00';
            $itemEnd = Carbon::parse($item)->endOfMonth()->toDateString() . '23:59:59';
            foreach ($dataDb as $itemDb) {
                $itemDbCreateTime = Carbon::parse($itemDb['create_time'])->toDateTimeString();
                if ($itemDbCreateTime >= $itemStart && $itemDbCreateTime <= $itemEnd) {
                    $itemCount += 1;
                    if ($itemDb['satisfaction'] >= 4) {
                        $itemSatisfactionCount += 1;
                    }
                }
            }
            $itemProbability = 0;
            if ($itemSatisfactionCount > 0 && $itemCount > 0) {
                $itemProbability = round($itemSatisfactionCount / $itemCount, 2);
            }
            $data[] = $itemProbability;
        }
        return $data;
    }

    // 获取x轴 labels 数据
    public function getXAxis($type, $monthStart = '', $monthEnd = '')
    {
        $now = Carbon::now();
        if ($type == self::DATE_TYPE_WEEK) {
            return [
                $now->startOfWeek()->toDateString(),
                $now->startOfWeek()->addDays(1)->toDateString(),
                $now->startOfWeek()->addDays(2)->toDateString(),
                $now->startOfWeek()->addDays(3)->toDateString(),
                $now->startOfWeek()->addDays(4)->toDateString(),
                $now->startOfWeek()->addDays(5)->toDateString(),
                $now->startOfWeek()->addDays(6)->toDateString()
            ];
        }
        if ($type == self::DATE_TYPE_MONTH) {
            $currentMonth = $now->format('Y-m');
            return [
                $currentMonth . ' 第1周',
                $currentMonth . ' 第2周',
                $currentMonth . ' 第3周',
                $currentMonth . ' 第4周'
            ];
        }
        if ($type == self::DATE_TYPE_QUARTER) {
            $currentMonth = intval($now->format('m'));
            $currentYear = $now->format('Y');
            if ($currentMonth >= 1 && $currentMonth <= 3) {
                return [
                    $currentYear . '-01',
                    $currentYear . '-02',
                    $currentYear . '-03'
                ];
            } elseif ($currentMonth >= 4 && $currentMonth <= 6) {
                return [
                    $currentYear . '-04',
                    $currentYear . '-05',
                    $currentYear . '-06'
                ];
            } elseif ($currentMonth >= 7 && $currentMonth <= 9) {
                return [
                    $currentYear . '-07',
                    $currentYear . '-08',
                    $currentYear . '-09'
                ];
            } else {
                return [
                    $currentYear . '-10',
                    $currentYear . '-11',
                    $currentYear . '-12'
                ];
            }
        }
        if ($type == self::DATE_TYPE_YEAR) {
            $currentYear = $now->format('Y');
            return [
                $currentYear . '-01',
                $currentYear . '-02',
                $currentYear . '-03',
                $currentYear . '-04',
                $currentYear . '-05',
                $currentYear . '-06',
                $currentYear . '-07',
                $currentYear . '-08',
                $currentYear . '-09',
                $currentYear . '-10',
                $currentYear . '-11',
                $currentYear . '-12'
            ];
        }
        if ($type == self::DATE_TYPE_MONTH_SELECT) {
            if (!$monthStart) {
                $monthStart = Carbon::now()->format('Y-m');
            }
            if (!$monthEnd) {
                $monthEnd = Carbon::now()->format('Y-m');
            }
            try {
                $monthStart = Carbon::parse($monthStart)->addMonths(-1)->format('Y-m');
            } catch (\Exception $e) {
                $monthStart = Carbon::now()->addMonths(-1)->format('Y-m');
            }
            try {
                $monthEnd = Carbon::parse($monthEnd)->format('Y-m');
            } catch (\Exception $e) {
                $monthStart = Carbon::now()->format('Y-m');
            }
            $allMonth = [];
            foreach (CarbonPeriod::create($monthStart, '1 month', $monthEnd) as $month) {
                $allMonth[] = $month->format('Y-m');
            }
            return $allMonth;
        }
    }

    public function target()
    {
        $event = $this->targetEvent();
        $workOrder = $this->targetWorkOrder();
        return [
            'event' => $event,
            'work_order' => $workOrder
        ];
    }

    public function todo($type = 'all')
    {
        $processEventBuilder = Event::query()->where('status', '<>', EventRepo::STATUS_COMPLETE);
        if ($type == self::TYPE_CREATOR) {
            $processEventBuilder->where('created_type', EventRepo::CREATED_USER)
                ->where('created_by', $this->userId);
        }
        if ($type == self::TYPE_ASSIST) {
            $processEventBuilder->join('event_user as eu', 'eu.event_id', '=', 'event.id')
                ->where('eu.user_id', '=', $this->userId)
                ->where('eu.is_master', 0);
        }
        if ($type == self::TYPE_MASTER) {
            $processEventBuilder->join('event_user as eu', 'eu.event_id', '=', 'event.id')
                ->where('eu.user_id', '=', $this->userId)
                ->where('eu.is_master', 1);
        }
        $processEventCount = $processEventBuilder->count();

        $unCallbackBuilder = WorkOrder::query()
            ->select(DB::raw('count(*) as count'))
            ->from('work_order as wo')
            ->join('work_order_callback as woc', 'woc.work_order_id', '=', 'wo.id');
        if ($type == self::TYPE_CREATOR) {
            $unCallbackBuilder->where('wo.create_user_id', $this->userId);
        }
        if ($type == self::TYPE_ASSIST) {
            $unCallbackBuilder->join('work_order_user as wou', 'wou.work_order_id', '=', 'wo.id')
                ->where('wou.user_id', $this->userId)
                ->where('wou.is_master', '=', 0);
        }
        if ($type == self::TYPE_MASTER) {
            $unCallbackBuilder->join('work_order_user as wou', 'wou.work_order_id', '=', 'wo.id')
                ->where('wou.user_id', $this->userId)
                ->where('wou.is_master', '=', 1);
        }
        $unCallbackWorkOrder = $unCallbackBuilder->first();
        $unCallbackWorkOrderCount = $unCallbackWorkOrder->count ?? 0;

        $processWorkOrderBuilder = WorkOrder::query()->where('status', WorkOrderRepo::STATUS_BEGIN);
        if ($type == self::TYPE_CREATOR) {
            $processWorkOrderBuilder->where('work_order.create_user_id', '=', $this->userId);
        }
        if ($type == self::TYPE_ASSIST) {
            $processWorkOrderBuilder->join('work_order_user as wou', 'wou.work_order_id', '=', 'work_order.id')
                ->where('wou.user_id', $this->userId)
                ->where('wou.is_master', '=', 0);
        }
        if ($type == self::TYPE_MASTER) {
            $processWorkOrderBuilder->join('work_order_user as wou', 'wou.work_order_id', '=', 'work_order.id')
                ->where('wou.user_id', $this->userId)
                ->where('wou.is_master', '=', 1);
        }

        $processWorkOrderCount = $processWorkOrderBuilder->count();
        $bomCount = 0;
        return [
            'type' => $type,
            'data' => [
                'process_event_count' => $processEventCount,
                'un_callback_work_order_count' => $unCallbackWorkOrderCount,
                'process_work_order_count' => $processWorkOrderCount,
                'bom_count' => $bomCount
            ]
        ];
    }

    private function targetWorkOrder()
    {
        $unCompleteCount = WorkOrder::query()->where('status', '<>', WorkOrderRepo::STATUS_COMPLETE)->count();
        $processCount = WorkOrder::query()->where('status', '=', WorkOrderRepo::STATUS_BEGIN)->count();
        // 异常(超时)工单: 计划时间 <= 当前时间 且状态位于已开始之前(不等于: 已开始、已完成、已关闭)
        $exceptionCount = WorkOrder::query()
            ->where('plan_at', '<=', Carbon::now()->toDateTimeString())
            ->where('status', '<>', WorkOrderRepo::STATUS_COMPLETE)
            ->where('status', '<>', WorkOrderRepo::STATUS_BEGIN)
            ->where('status', '<>', WorkOrderRepo::STATUS_CLOSED)
            ->count();
        // 本月服务客户数量
        $currentMonth = $this->currentMonth();
        $currentMonthData = WorkOrder::query()
            ->select(DB::raw('count(distinct customer_id) as count'))
            ->where('update_time', '>=', $currentMonth['start'])
            ->where('update_time', '<=', $currentMonth['end'])
            ->first();
        $currentMonthServiceCount = $currentMonthData->count ?? 0;
        // 上月服务客户数量
        $prevMonth = $this->prevMonth();
        $prevMonthData = WorkOrder::query()
            ->select(DB::raw('count(distinct customer_id) as count'))
            ->where('update_time', '>=', $prevMonth['start'])
            ->where('update_time', '<=', $prevMonth['end'])
            ->first();
        $prevMonthServiceCount = $prevMonthData->count ?? 0;
        if ($prevMonthServiceCount == 0) {
            $percent = 1;
        } else {
            $percent = ($currentMonthServiceCount - $prevMonthServiceCount) / $prevMonthServiceCount;
        }
        return [
            'un_complete' => $unCompleteCount,
            'process' => $processCount,
            'exception' => $exceptionCount,
            'month_service_count' => $currentMonthServiceCount,
            'percent' => round(round($percent, 4) * 100, 2)
        ];
    }

    private function targetEvent()
    {
        $today = $this->today();
        $yesterday = $this->yesterday();
        $unCompleteCount = Event::query()->where('status', '<>', EventRepo::STATUS_COMPLETE)->count();
        $processCount = Event::query()->where('status', '=', EventRepo::STATUS_PROCESSING)->count();
        // 今日完成数量
        $todayCompleteCount = Event::query()
            ->where('status', '=', EventRepo::STATUS_COMPLETE)
            ->where('update_time', '>=', $today['start'])
            ->where('update_time', '<=', $today['end'])
            ->count();
        // 昨日完成数量
        $yesterdayCompleteCount = Event::query()
            ->where('status', '=', EventRepo::STATUS_COMPLETE)
            ->where('update_time', '>=', $yesterday['start'])
            ->where('update_time', '<=', $yesterday['end'])
            ->count();
        // 本月服务客户数量
        $currentMonth = $this->currentMonth();
        $currentMonthData = Event::query()
            ->select(DB::raw('count(distinct customer_id) as count'))
            ->where('update_time', '>=', $currentMonth['start'])
            ->where('update_time', '<=', $currentMonth['end'])
            ->first();
        $currentMonthServiceCount = $currentMonthData->count ?? 0;

        $prevMonth = $this->prevMonth();
        $prevMonthData = Event::query()
            ->select(DB::raw('count(distinct customer_id) as count'))
            ->where('update_time', '>=', $prevMonth['start'])
            ->where('update_time', '<=', $prevMonth['end'])
            ->first();
        $prevMonthServiceCount = $prevMonthData->count ?? 0;
        if ($prevMonthServiceCount == 0) {
            $percent = 1;
        } else {
            $percent = ($currentMonthServiceCount - $prevMonthServiceCount) / $prevMonthServiceCount;
        }

        return [
            'un_complete' => $unCompleteCount,
            'process' => $processCount,
            'today_complete' => $todayCompleteCount,
            'yesterday_complete' => $yesterdayCompleteCount,
            'month_service_count' => $currentMonthServiceCount,
            'percent' => round($percent, 4) * 100
        ];
    }

    private function today()
    {
        $today = Carbon::now()->toDateString();
        $todayStart = $today . ' 00:00:00';
        $todayEnd = $today . ' 23:59:59';

        return [
            'start' => $todayStart,
            'end' => $todayEnd
        ];
    }

    private function yesterday()
    {
        $yesterday = Carbon::now()->addDays(-1)->toDateString();
        $yesterdayStart = $yesterday . ' 00:00:00';
        $yesterdayEnd = $yesterday . ' 23:59:59';

        return [
            'start' => strtotime($yesterdayStart),
            'end' => strtotime($yesterdayEnd)
        ];
    }

    private function currentMonth()
    {
        $first = Carbon::now()->startOfMonth()->toDateString() . ' 00:00:00';
        $last = Carbon::now()->toDateString() . ' 23:59:59';
        return [
            'start' => strtotime($first),
            'end' => strtotime($last)
        ];
    }

    private function prevMonth()
    {
        $first = Carbon::now()->addMonths(-1)->startOfMonth()->toDateString() . ' 00:00:00';
        $last = Carbon::now()->addMonths(-1)->endOfMonth()->toDateString() . ' 23:59:59';
        return [
            'start' => strtotime($first),
            'end' => strtotime($last)
        ];
    }
}